package script

import (
	"errors"
	"fmt"
	"gitee.com/fierce_wolf/go-fox-edge-common/commEntity"
	"gitee.com/fierce_wolf/go-fox-edge-common/commRedisService"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Hex"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Method"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/SyncMap"
	"github.com/dop251/goja"
	"sync"
)

type scriptEngineService struct {
	engineMap sync.Map
}

func (e *scriptEngineService) getScriptEngine(manufacturer string, deviceType string, operateName string) *goja.Runtime {
	engine, err := SyncMap.GetValue(&e.engineMap, manufacturer, deviceType, operateName)
	if err != nil || engine == nil {
		engine = goja.New()
		SyncMap.SetValue(&e.engineMap, manufacturer, deviceType, operateName, engine)
	}

	return engine.(*goja.Runtime)
}

func (e *scriptEngineService) buildSplitOperate(operateEntity *commEntity.OperateEntity) (dec *Decoder, err error) {
	// 定义一个最终的异常拦截，用来防止可能的代码BUG
	defer func() {
		if r := recover(); r != nil {
			dec = nil
			err = errors.New(fmt.Sprintf("捕获到异常:%v", r))
		}
	}()

	// 检测：收发为JSP引擎
	if operateEntity.EngineType != "JavaScript" {
		return nil, errors.New("获得操作实体，不是一个Jsp引擎方法")
	}
	if operateEntity.OperateName != "splitHandler" {
		return nil, errors.New("获得操作实体，不是一个Jsp引擎方法")
	}
	if operateEntity.OperateMode != "splitHandler" {
		return nil, errors.New("获得操作实体，不是一个Jsp引擎方法")
	}
	if operateEntity.ServiceType != "channel" {
		return nil, errors.New("获得操作实体，不是一个Jsp引擎方法")
	}

	// 检测：引擎参数是否正确
	decode := operateEntity.EngineParam["decode"]
	if Method.HasEmpty(decode) {
		return nil, errors.New("engineParam为空")
	}

	// 检测：脚本是否正确
	decodeMap := decode.(map[string]interface{})
	decodeScript := decodeMap["code"]
	main := decodeMap["main"]
	if Method.HasEmpty(main, decodeScript) {
		return nil, errors.New("未定义解码脚本")
	}

	// 获得引擎
	scriptEngine := e.getScriptEngine(operateEntity.Manufacturer, operateEntity.DeviceType, operateEntity.OperateName)

	// 装载脚本
	scriptEngine.RunString(decodeScript.(string))

	// 验证脚本：是否能够基本执行
	pack := make([]byte, 16)
	message := Hex.EncodeToString(pack)

	// 执行JSP脚本中的函数
	decodeMethod, ok := goja.AssertFunction(scriptEngine.Get("decode"))
	if !ok {
		return nil, errors.New("找不到decode()函数")
	}

	// 执行函数
	_, err = decodeMethod(goja.Undefined(), scriptEngine.ToValue(message))
	if err != nil {
		return nil, errors.New("尝试执行decode()失败")
	}

	scriptSplitMessage := &Decoder{}
	scriptSplitMessage.engine = scriptEngine
	scriptSplitMessage.script = decodeScript.(string)
	scriptSplitMessage.format = "Hex"

	return scriptSplitMessage, nil
}

func (e *scriptEngineService) buildServiceKeyOperate(operateEntity *commEntity.OperateEntity) (dec *Decoder, err error) {
	// 定义一个最终的异常拦截，用来防止可能的代码BUG
	defer func() {
		if r := recover(); r != nil {
			dec = nil
			err = errors.New(fmt.Sprintf("捕获到异常:%v", r))
		}
	}()

	// 检测：收发为JSP引擎
	if operateEntity.EngineType != "JavaScript" {
		return nil, errors.New("获得操作实体，不是一个Jsp引擎方法")
	}
	if operateEntity.OperateName != "keyHandler" {
		return nil, errors.New("获得操作实体，不是一个Jsp引擎方法")
	}
	if operateEntity.OperateMode != "keyHandler" {
		return nil, errors.New("获得操作实体，不是一个Jsp引擎方法")
	}
	if operateEntity.ServiceType != "channel" {
		return nil, errors.New("获得操作实体，不是一个Jsp引擎方法")
	}

	// 检测：引擎参数是否正确
	decode := operateEntity.EngineParam["decode"]
	if Method.HasEmpty(decode) {
		return nil, errors.New("engineParam为空")
	}

	// 检测：脚本是否正确
	decodeMap := decode.(map[string]interface{})
	decodeScript := decodeMap["code"]
	main := decodeMap["main"]
	if Method.HasEmpty(main, decodeScript) {
		return nil, errors.New("未定义解码脚本")
	}

	// 获得引擎
	scriptEngine := e.getScriptEngine(operateEntity.Manufacturer, operateEntity.DeviceType, operateEntity.OperateName)

	// 装载脚本
	scriptEngine.RunString(decodeScript.(string))

	// 验证脚本：是否能够基本执行
	pack := make([]byte, 16)
	message := Hex.EncodeToString(pack)

	// 执行JSP脚本中的函数
	decodeMethod, ok := goja.AssertFunction(scriptEngine.Get("decode"))
	if !ok {
		return nil, errors.New("找不到decode()函数")
	}

	// 执行函数
	_, err = decodeMethod(goja.Undefined(), scriptEngine.ToValue(message))
	if err != nil {
		return nil, errors.New("尝试执行decode()失败")
	}

	scriptSplitMessage := &Decoder{}
	scriptSplitMessage.engine = scriptEngine
	scriptSplitMessage.script = decodeScript.(string)
	scriptSplitMessage.format = "Hex"

	return scriptSplitMessage, nil
}

func (e *scriptEngineService) getSplitOperate(channelParam map[string]interface{}) (entity *commEntity.OperateEntity, err error) {
	// 定义一个最终的异常拦截，用来防止可能的代码BUG
	defer func() {
		if r := recover(); r != nil {
			entity = nil
			err = errors.New(fmt.Sprintf("捕获到异常:%v", r))
		}
	}()

	manufacturer := channelParam["manufacturer"]
	deviceType := channelParam["deviceType"]
	splitHandler := channelParam["splitHandler"]
	if Method.HasEmpty(manufacturer, deviceType, splitHandler) {
		return nil, errors.New("参数不能为空: manufacturer, deviceType, splitHandler")
	}

	operateEntity := commEntity.Type.OperateEntity.NewEntity().(*commEntity.OperateEntity)
	operateEntity.Manufacturer = manufacturer.(string)
	operateEntity.DeviceType = deviceType.(string)
	operateEntity.OperateName = splitHandler.(string)
	operateEntity.EngineType = "JavaScript"

	exist, err := commRedisService.GetEntityByServiceKey(operateEntity.Type(), operateEntity.MakeServiceKey())
	if err != nil || exist == nil {
		return nil, errors.New("参数不能为空: 找不到对应的操作实体")
	}

	return exist.(*commEntity.OperateEntity), nil
}

func (e *scriptEngineService) getKeyOperate(channelParam map[string]interface{}) (entity *commEntity.OperateEntity, err error) {
	// 定义一个最终的异常拦截，用来防止可能的代码BUG
	defer func() {
		if r := recover(); r != nil {
			entity = nil
			err = errors.New(fmt.Sprintf("捕获到异常:%v", r))
		}
	}()

	manufacturer := channelParam["manufacturer"]
	deviceType := channelParam["deviceType"]
	keyHandler := channelParam["keyHandler"]
	serviceKey := channelParam["serviceKey"]
	if Method.HasEmpty(manufacturer, deviceType, keyHandler, serviceKey) {
		return nil, errors.New("参数不能为空: manufacturer, deviceType, keyHandler, serviceKey")
	}

	operateEntity := commEntity.Type.OperateEntity.NewEntity().(*commEntity.OperateEntity)
	operateEntity.Manufacturer = manufacturer.(string)
	operateEntity.DeviceType = deviceType.(string)
	operateEntity.OperateName = keyHandler.(string)
	operateEntity.EngineType = "JavaScript"

	exist, err := commRedisService.GetEntityByServiceKey(operateEntity.Type(), operateEntity.MakeServiceKey())
	if err != nil || exist == nil {
		return nil, errors.New("参数不能为空: 找不到对应的操作实体")
	}

	return exist.(*commEntity.OperateEntity), nil
}
